﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GoldDigger.Models;
using GoldDigger.Sprites;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using TPL.Xna.Common;

namespace GoldDigger.Screens
{
    internal class PlayScreen : Screen, ISceneManager
    {
        /// <summary>
        /// Keep current scene information
        /// </summary>
        private Scene _scene;

        private int _level;

        /// <summary>
        /// The height of pully.
        /// </summary>
        private const int WoodChainY = 100;

        /// <summary>
        /// The hook sprite
        /// </summary>
        private HookSprite _hook;

        /// <summary>
        /// Keep for updating point
        /// </summary>
        private TextSprite _pointText;

        /// <summary>
        /// Keep for updaing clock.
        /// </summary>
        private ClockSprite _clockSprite;

        /// <summary>
        /// Construct with a scene model.
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="totalValue"></param>
        /// <param name="level"></param>
        public PlayScreen(Scene scene, int totalValue, int level)
        {
            _scene = scene;
            _level = level;
            TotalValue = totalValue;
        }

        /// <summary>
        /// Load every sprites including static and scene specific.
        /// </summary>
        public override void LoadContent()
        {
            // Add sprites so scene
            AddBackground();
            AddWoodChain();
            AddPulley();
            AddHook();
            AddItems();

            // Do initialization
            base.LoadContent();

            // Start the clock after all
            _clockSprite.Start(_scene.PlayTime);
        }

        /// <summary>
        /// Add background and texts.
        /// </summary>
        private void AddBackground()
        {
            AddSprite(new BackgroundSprite(ScreenManager.Game.Content.Load<Texture2D>(@"Images/bg")));

            // Common font
            var font = ScreenManager.Game.Content.Load<SpriteFont>(@"Fonts/PlayScreenText");

            Func<string, TextSprite> createText =
                t => new TextSprite(t)
                         {
                             Color = Color.Yellow,
                             Font = font,
                             ZIndex = 1f,
                         };

            const float firstRowY = 10;
            const float secondRowY = 60;
            const float firstColX = 10;
            const float secondColX = 630;

            // Point text
            AddSprite(createText("Current: ")
                .SetPosision(new Vector2(firstColX, firstRowY)));
            AddSprite(_pointText = (TextSprite)createText(TotalValue.ToString())
                .SetPosision(new Vector2(firstColX + 130, firstRowY)));

            // Target text
            AddSprite(createText("Target: ")
                .SetPosision(new Vector2(firstColX, secondRowY)));
            AddSprite(createText(_scene.TargetValue.ToString())
                .SetPosision(new Vector2(firstColX + 130, secondRowY))
                .SetColor(Color.Green));

            // Time
            AddSprite(createText("Time: ")
                .SetPosision(new Vector2(secondColX, firstRowY)));
            AddSprite(_clockSprite = new ClockSprite(this)
                                         {
                                             Color = Color.Yellow,
                                             Font = font,
                                             ZIndex = 1f,
                                             Position = new Vector2(secondColX + 90, firstRowY)

                                         });
            // Level
            AddSprite(createText("Level: ")
                .SetPosision(new Vector2(secondColX, secondRowY)));
            AddSprite(createText(_level.ToString())
                .SetPosision(new Vector2(secondColX + 90, secondRowY)));
        }

        /// <summary>
        /// Add basement for placing pully.
        /// </summary>
        private void AddWoodChain()
        {
            const float scale = 0.4f;
            var image = ScreenManager.Game.Content.Load<Texture2D>(@"Images/wood");
            var x = 0;
            var width = image.Width * scale - 10;
            var count = Math.Ceiling(
                ScreenManager.GraphicsDevice.PresentationParameters.BackBufferWidth / width);
            for (int i = 0; i < count; i++, x += (int)width)
                AddSprite(new ImageSprite(image)
                              {
                                  Position = new Vector2(x, WoodChainY),
                                  Scale = scale,
                                  ZIndex = 0.1f,
                              });
        }

        /// <summary>
        /// Add pulley
        /// </summary>
        private void AddPulley()
        {
            const float scale = 0.4f;
            var image = ScreenManager.Game.Content.Load<Texture2D>(@"Images/pulley");
            var width = image.Width * scale;
            var height = image.Height * scale;
            var x = (ScreenManager.GraphicsDevice.PresentationParameters.BackBufferWidth - width) / 2;
            var y = WoodChainY - height + 12;
            AddSprite(new ImageSprite(image)
                          {
                              Position = new Vector2(x, y),
                              Scale = scale,
                              ZIndex = 0.1f
                          });
        }

        /// <summary>
        /// Add hook
        /// </summary>
        private void AddHook()
        {
            const float scale = 1.5f;
            var image = ScreenManager.Game.Content.Load<Texture2D>(@"Images/hook");
            var ropeRectImage = ScreenManager.Game.Content.Load<Texture2D>(@"Images/rect");

            _hook = new HookSprite(this, image, new Vector2((float)image.Width / 2, 0), ropeRectImage)
                        {
                            Position = new Vector2(400, 90),
                            Scale = scale,
                            ZIndex = 0.3f,
                        };
            AddSprite(_hook);
        }

        /// <summary>
        /// Add scene items.
        /// </summary>
        private void AddItems()
        {
            foreach (var item in _scene.ItemSprites)
            {
                try
                {
                    AddSprite(ScreenManager.Game.Content.Load(item));
                }
                catch (NotImplementedException)
                {
                    // DO nothing
                }
            }
        }

        /// <summary>
        /// User tap to launch hook.
        /// Tap is not accepted while launching of pulling.
        /// </summary>
        /// <param name="input"></param>
        public override void HandleInput(InputState input)
        {
            var touchState = TouchPanel.GetState();

            //interpret touch screen presses
            foreach (TouchLocation location in touchState)
            {
                switch (location.State)
                {
                    case TouchLocationState.Pressed:
                        if (_hook.HookState == HookState.Root)
                            _hook.HookState = HookState.Lauch;
                        break;
                    case TouchLocationState.Moved:
                        break;
                    case TouchLocationState.Released:
                        break;
                }
            }
            base.HandleInput(input);
        }

        /// <summary>
        /// Handle end of scene if there is no more spite to collect.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            // No more item to take, handle end of scene
            if (Sprites.OfType<ItemSprite>().Count() == 0)
                HandleEndScene();

            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
        }

        #region ISceneManager implementation

        /// <summary>
        /// Collect an item out of screen.
        /// Add its point to total point.
        /// </summary>
        /// <param name="item"></param>
        public void CollectItem(ItemSprite item)
        {
            // Add point
            TotalValue += item.Value;
            // Update point on screen
            _pointText.SetText(TotalValue.ToString());
            // Remove the item
            RemoveSprite(item);
        }

        /// <summary>
        /// Handle timeup
        /// </summary>
        public void HandlSceneTimeUp()
        {
            HandleEndScene();
        }

        /// <summary>
        /// If total value is greater than target value then move to next scene.
        ///     In case, there is no next scene then user win.
        /// Else user loose
        /// </summary>
        public void HandleEndScene()
        {
            if (TotalValue < _scene.TargetValue)
                ScreenManager.AddScreen(new GameOverScreen(TotalValue, _scene.TargetValue));
            else
            {
                Scene nextScene = null;
                _level++;
                try
                {
                    nextScene = ScreenManager.Game.Content.Load<Scene>(@"Scenes/scene" + _level);
                }
                catch (ContentLoadException)
                {
                    // No more scene
                }

                // ScreenManager.Game.Content.LoadScene(_scene.Level++);
                if (nextScene != null)
                    ScreenManager.AddScreen(new WelcomeScreen(nextScene, TotalValue, _level));
                else
                    ScreenManager.AddScreen(new GameOverScreen(TotalValue, _scene.TargetValue));
            }
        }

        /// <summary>
        /// Total value.
        /// </summary>
        public int TotalValue { get; set; }

        #endregion
    }
}
